package com.nintendo.socket;


import com.a3test.component.idworker.IdWorkerConfigBean;
import com.a3test.component.idworker.Snowflake;
import com.nintendo.enums.MsgTypeEnum;
import com.nintendo.mq.MessagePublisher;
import com.nintendo.pojo.netty.ChatMsg;
import com.nintendo.pojo.netty.DataContent;
import com.nintendo.pojo.netty.NettyServerNode;
import com.nintendo.result.GraceJSONResult;
import com.nintendo.utils.JedisPoolUtil;
import com.nintendo.utils.JsonUtils;
import com.nintendo.utils.LocalDateUtils;
import com.nintendo.utils.ZookeeperRegister;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 创建自定义助手类
 *
 * @Auther 风间影月
 */
// SimpleChannelInboundHandler: 对于请求来说，相当于入站(入境)
// TextWebSocketFrame: 用于为websocket专门处理的文本数据对象，Frame是数据(消息)的载体
@Slf4j
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    // 用于记录和管理所有客户端的channel组
    public static ChannelGroup clients =
            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx,
                                TextWebSocketFrame msg) throws Exception {
        // 获得客户端传输过来的消息
        String content = msg.text();
        System.out.println("接受到的数据：" + content);

        // 1. 获取客户端发来的消息并且解析
        DataContent dataContent = JsonUtils.jsonToPojo(content, DataContent.class);
        ChatMsg chatMsg = dataContent.getChatMsg();

        String msgText = chatMsg.getMsg();
        String receiverId = chatMsg.getReceiverId();
        String senderId = chatMsg.getSenderId();

        // 判断是否黑名单 start
        // 如果双方只要有一方是黑名单，则终止发送
//        GraceJSONResult result = OkHttpUtil.get("http://127.0.0.1:1000/friendship/isBlack?friendId1st=" + receiverId
//                                                                + "&friendId2nd=" + senderId);
//        boolean isBlack = (Boolean)result.getData();
//        System.out.println("当前的黑名单关系为: " + isBlack);
//        if (isBlack) {
//            return;
//        }
        // 判断是否黑名单 end

        // 时间校准，以服务器的时间为准
        chatMsg.setChatTime(LocalDateTime.now());

        Integer msgType = chatMsg.getMsgType();

        // 获取channel
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        String currentChannelIdShort = currentChannel.id().asShortText();
//        GraceJSONResult graceJSONResult = OkHttpUtil.get("http://localhost:8080/friendShip/isBlack?myId=" + senderId + "&friendId=" + receiverId);
//        System.out.println("================graceJSONResult>>"+graceJSONResult);
        // System.out.println("客户端currentChannelId：" + currentChannelId);
        // System.out.println("客户端currentChannelIdShort：" + currentChannelIdShort);

        // 2. 判断消息类型，根据不同的类型来处理不同的业务
        if (msgType == MsgTypeEnum.CONNECT_INIT.type) {
            // 当websocket初次open的时候，初始化channel，把channel和用户userid管来起来
            UserChannelSession.putMultiChannels(senderId, currentChannel);
            UserChannelSession.putUserChannelIdRelation(currentChannelId, senderId);
            // 初次连接后该节点下的连接人数累加
            NettyServerNode minNode = dataContent.getServerNode();
            ZookeeperRegister.incrementOnLineCounts(minNode);
            // 获得ip和端口在redis中设置关系 以便在前端设备短线后减少在线人数
            Jedis jedis = JedisPoolUtil.getJedis();
            jedis.set(senderId,JsonUtils.objectToJson(minNode));
        } else if (msgType == MsgTypeEnum.WORDS.type
                || msgType == MsgTypeEnum.IMAGE.type
                || msgType == MsgTypeEnum.VIDEO.type
                || msgType == MsgTypeEnum.VOICE.type
        ) {
            Snowflake snowflake = new Snowflake(new IdWorkerConfigBean());
            String sid = snowflake.nextId();
            chatMsg.setMsgId(sid);
            List<Channel> receiverChannels = UserChannelSession.getMultiChannels(receiverId);
            if (receiverChannels == null || receiverChannels.isEmpty() || receiverChannels.size() == 0) {
                chatMsg.setIsReceiverOnLine(false);
                log.info("当前用户：" + receiverId + "不在线，发送消息给离线用户");
            } else {
                chatMsg.setIsReceiverOnLine(true);
                log.info("当前用户：" + receiverId + "在线，发送消息给在线用户");
                for (Channel receiverChannel : receiverChannels) {
                    Channel findChannel = clients.find(receiverChannel.id());
                    if (findChannel != null) {
                        if (msgType == MsgTypeEnum.VOICE.type) {
                            chatMsg.setIsRead(false);
                        }
                        dataContent.setChatMsg(chatMsg);
                        String chatTimeFormat = LocalDateUtils
                                .format(chatMsg.getChatTime(),
                                        LocalDateUtils.DATETIME_PATTERN_2);
                        dataContent.setChatTime(chatTimeFormat);
                        // 发送消息给在线的用户
                        findChannel.writeAndFlush(
                                new TextWebSocketFrame(
                                        JsonUtils.objectToJson(dataContent)));
                    }
                }
            }
            System.out.println("保存消息===============================>"+chatMsg);
           MessagePublisher.sendMsgToSave(chatMsg);
        }

//         currentChannel.writeAndFlush(new TextWebSocketFrame(currentChannelId));


        // clients.writeAndFlush(new TextWebSocketFrame(currentChannelId));

        UserChannelSession.outputMulti();
    }

    /**
     * 客户端连接到服务端之后(打开链接)
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        System.out.println("客户端建立连接，channel对应的长id为：" + currentChannelId);
        // 获得客户端的channel，并且存入到ChannelGroup中进行管理(作为一个客户端群组)
        clients.add(currentChannel);
    }

    /**
     * 关闭连接，移除channel
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        System.out.println("客户端关闭连接，channel对应的长id为：" + currentChannelId);

        // 移除多余的会话
        String userId = UserChannelSession.getUserIdByChannelId(currentChannelId);
        UserChannelSession.removeUselessChannels(userId, currentChannelId);

        clients.remove(currentChannel);
        Jedis jedis = JedisPoolUtil.getJedis();
        NettyServerNode minNode = JsonUtils.jsonToPojo(jedis.get(userId), NettyServerNode.class);
        ZookeeperRegister.decrementOnLineCounts(minNode);
    }

    /**
     * 发生异常并且捕获，移除channel
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        System.out.println("发生异常捕获，channel对应的长id为：" + currentChannelId);
        // 区分异常类型处理
        if (cause instanceof DecoderException) {
            // 如果是解码异常（如心跳包），忽略不关闭连接
            log.warn("无害解码异常: {}", cause.getMessage());
            return;
        }

        log.error("严重异常捕获，关闭连接: {}", currentChannelId, cause);
        // 发生异常之后关闭连接(关闭channel)
        ctx.channel().close();
        // 随后从ChannelGroup中移除对应的channel
        clients.remove(currentChannel);

        // 移除多余的会话
        String userId = UserChannelSession.getUserIdByChannelId(currentChannelId);
        UserChannelSession.removeUselessChannels(userId, currentChannelId);
        // 在线人数累减
        Jedis jedis = JedisPoolUtil.getJedis();
        NettyServerNode minNode = JsonUtils.jsonToPojo(jedis.get(userId), NettyServerNode.class);
        ZookeeperRegister.decrementOnLineCounts(minNode);
    }
}
